package utils
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.Sprite;
	import flash.geom.ColorTransform;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.utils.ByteArray;
	import flash.utils.describeType;
	import flash.utils.getDefinitionByName;
	import flash.utils.getQualifiedClassName;
	
	/**
	 * 1) Создавайте константы для часто используемых объектов типа new Point(0,0) или new Rectangle(0,0,320,240):
	
	   //вызов 100000 раз: 75мс
	   new Point(0,0);
	   //или
	   private static const POINT:Point = new Point(0,0);
	   //8мс
	   POINT;
	
	   Быстрее на 838%
	
	   2) Сократите указания на статические члены классов. Вместо этого старайтесь использовать переменные пакета:
	   //15мсSomeClass.somevar;package somepackage{public const somevar:int;}//8мсsomevar;
	   на 66% быстрее
	
	   3) Сохраняйте getter свойства в локальных переменных при их использовании более одного раза.
	
	   somesprite.graphics.clear();
	   somesprite.graphics.beginFill(0x000000);
	   somesprite.graphics.drawRect(0,0,10,10);
	   somesprite.graphics.endFill();
	
	   // лучше заменить на
	
	   var n:Graphics = somesprite.graphics;
	   n.clear();
	   n.beginFill(0x000000);
	   n.drawRect(0,0,10,10);
	   n.endFill();
	
	   4) Создавайте собственные reflection-методы вместо использования getDefinitionByName(getQualifiedClassName(object))
	   utils reflection// 503мсvar someclass:Class = getDefinitionByName(getQualifiedClassName(someObject));// getter reflectionpublic class SomeClass {public function get reflect():Class { return SomeClass;}}// 9мсvar someclass:Class = object.reflect();
	   быстрее на 5489%
	
	   5) Страрайтесь использовать строгое типизирование
	
	   6) Приводите к нужным типам, например, при чтении из массива.
	   Быстрее будет обращаться к image
	
	   var image: BitmapData = images[index];
	   image.getPixel(x,y)
	   //чем к
	   images[index].getPixel(x,y);
	
	   7) Где возможно, используйте int вместо Number
	
	   8) Операторы побитового сдвига работают быстрее:
	
	   Побитовый сдвиг влево быстрее на 300% при умножении на любую степень двойки:
	
	   x = x * 2;
	   x = x * 64;
	   //равно:
	   x = x << 1;
	   x = x << 6;
	
	   Побитовый сдвиг вправо быстрее на 350% при делении на любую степень двойки:
	
	   x = x / 2;
	   x = x / 64;
	   //равно:
	   x = x >> 1;
	   x = x >> 6;
	
	   Извлечение компонентов цвета:
	
	   //24bit
	   var color:uint = 0x336699;
	   var r:uint = color >> 16;
	   var g:uint = color >> 8 & 0xFF;
	   var b:uint = color & 0xFF;
	   //32bit
	   var color:uint = 0xff336699;
	   var a:uint = color >>> 24;
	   var r:uint = color >>> 16 & 0xFF;
	   var g:uint = color >>> 8 & 0xFF;
	   var b:uint = color & 0xFF;
	
	   Объединение компонентов цвета:
	
	   //24bit
	   var r:uint = 0x33;
	   var g:uint = 0x66;
	   var b:uint = 0x99;
	   var color:uint = r << 16 | g << 8 | b;
	
	   //32bit
	   var a:uint = 0xff;
	   var r:uint = 0x33;
	   var g:uint = 0x66;
	   var b:uint = 0x99;
	   var color:uint = a << 24 | r << 16 | g << 8 | b;
	
	   9) Обмен значениями численных переменных без использования временной переменной:
	
	   var t:int = a;
	   a = b;
	   b = t;
	   //равно:
	   a ^= b;
	   b ^= a;
	   a ^= b;
	
	   Быстрее на 20%.
	
	   10) Инкремент/декремент.
	   Внимание: это пример понижения производительности, но зато есть возможность понизить читабельность кода для врагов:)
	
	   i = -~i; // i++
	   i = ~-i; // i--
	
	   11) Смена знака с помощью NOT или XOR (говорят на 300% быстрее):
	
	   i = -i;
	   //равно:
	   i = ~i + 1;
	   //или
	   i = (i ^ -1) + 1;
	
	   12) Подсчет остатка
	
	   Если делитель является степенью двух, то остаток можно подсчитать быстрее на 600% следующим образом:
	   остаток = делимое & (делитель - 1);
	
	   x = 131 % 4;
	   //равно:
	   x = 131 & (4 - 1);
	
	   13) Проверка на четность/нечетность:
	
	   isEven = (i % 2) == 0;
	   //равно:
	   isEven = (i & 1) == 0;
	
	   Быстрее на 600%.
	
	   14) Абсолютное значение. Версия 1 быстрее на 250%, чем Math.abs(), а версия 2 быстрее на 20%, чем версия 1.
	
	   //версия 1
	   i = x < 0 ? -x : x;
	   //версия 2
	   i = (x ^ (x >> 31)) - (x >> 31);
	
	   15) Сравнение двух числе на равенство знаков
	
	   eqSign = a * b > 0;
	   //равно:
	   eqSign = a ^ b >= 0;
	
	   На 35% быстрее.
	
	   16) Быстрое конвертирование цвета из R5G5B5 в R8G8B8
	
	   R8 = (R5 << 3) | (R5 >> 2)
	   G8 = (R5 << 3) | (R5 >> 2)
	   B8 = (R5 << 3) | (R5 >> 2)
	
	   17) a++ быстрее, чем a = a + 1 и т.п.
	
	   18) Умножение быстрее деления.
	
	   Лучше использовать a = b*.5 вместо a = b / 2
	
	   19) int(1.5) быстрее, чем Math.floor(1.5)
	   Аналогично int(1.5) + 1 быстрее, чем Math.ceil(1.5)
	
	 * ...
	 * @author SChabanov
	 */
	public final class MyUtils
	{
		
		private static var _idCounter:int;
		
		public function MyUtils()
		{
			throw new Error("constructor not needed")
		}
		
		/**
		 *
		 * @param	value
		 * @param	max
		 * @return
		 */
		public static function shortByK(value:Number, max:int = 1000):String
		{
			var output:String;
			if (value >= max)
			{
				output = Math.floor(value / 1000) + "K";
			}
			else
			{
				output = value.toString();
			}
			return output;
		}
		
		public static function traceObject(object:Object = null):String
		{
			if (object == null)
				return "Object is null";
			var output:String = getQualifiedClassName(object) + "\n";
			
			for (var item:String in object)
			{
				output += item + "= " + object[item] + "\n";
			}
			return output;
		}
		
		/**
		 *
		 * @param	object
		 * @param	angleDegrees
		 */
		public static function rotateFromCenter(object:DisplayObject, angleDegrees:Number):void
		{
			var point:Point = new Point(object.x + object.width / 2, object.y + object.height / 2);
			var m:Matrix = object.transform.matrix;
			m.tx -= point.x;
			m.ty -= point.y;
			m.rotate(angleDegrees * (Math.PI / 180));
			m.tx += point.x;
			m.ty += point.y;
			object.transform.matrix = m;
		
		}
		
		/**
		 *
		 * @param	value
		 * @return
		 */
		public static function absolute(value:Number):Number
		{
			return (value ^ (value >> 31)) - (value >> 31);
		}
		
		/**
		 * returns a random value between min and max
		 * @param	min
		 * @param	max
		 * @return
		 */
		public static function randRange(min:Number, max:Number):Number
		{
			return Math.random() * (max - min) + min;
		
		}
		
		/**
		 * Convert seconds to time
		 * @param	seconds
		 * @return time in hh:mm or mm:ss format
		 */
		public static function secondsToTime(seconds:int):String
		{
			var days:int = 0;
			var hours:int = 0;
			var mins:int = 0;
			var secs:int = 0;
			
			var secsText:String;
			var minsText:String;
			var hoursText:String;
			
			// check whether the time is 0
			if (0 >= seconds)
				return '00:00';
			
			// check whether the time is less than hour
			if (3600 > seconds)
			{
				mins = seconds / 60;
				secs = seconds % 60;
				
				minsText = (mins < 10) ? '0' + mins.toString() : mins.toString();
				secsText = (secs < 10) ? '0' + secs.toString() : secs.toString();
				
				return (minsText + ':' + secsText);
			}
			
			hours = seconds / 3600;
			mins = (seconds % 3600) / 60;
			
			hoursText = (hours < 10) ? '0' + hours.toString() : hours.toString();
			minsText = (mins < 10) ? '0' + mins.toString() : mins.toString();
			return (hoursText + ':' + minsText);
		}
		
		/**
		 *
		 * @param	clip
		 */
		public static function rasterize(clip:Sprite):void
		{
			var clipContentsBound:Rectangle = clip.getBounds(clip);
			var bitmapData:BitmapData = new BitmapData(clipContentsBound.width, clipContentsBound.height, true, 0x00000000);
			var matrix:Matrix = new Matrix();
			matrix.translate(-clipContentsBound.x, -clipContentsBound.y);
			bitmapData.draw(clip, matrix);
			while (clip.numChildren > 0)
				clip.removeChildAt(0);
			clip.graphics.clear();
			var bitmap:Bitmap = new Bitmap(bitmapData);
			bitmap.x = clipContentsBound.x;
			bitmap.y = clipContentsBound.y;
			clip.addChild(bitmap);
		}
		
		/**
		 * Quadratic Bézier curves
		 * http://en.wikipedia.org/wiki/Bezier_curve
		 * @param	p0
		 * @param	p1
		 * @param	p2
		 * @param	step
		 * @return
		 */
		public static function getBezierQuadraticPoints(x0:Number,y0:Number,x1:Number,y1:Number,x2:Number,y2:Number,step:Number=1/100):Vector.<Point>
		{
				var vec:Vector.<Point> = new Vector.<Point>();
				var _x:Number;
				var _y:Number
				for (var t:Number = 0; t <= 1; t+=step) 
				{
					_x = Math.pow(1 - t, 2) * x0 + 2 * t * (1 - t) * x1 + Math.pow(t, 2) * x2;
					_y = Math.pow(1 - t, 2) * y0 + 2 * t * (1 - t) * y1 + Math.pow(t, 2) * y2;
					vec.push(new Point(_x,_y));
				}
				return vec;
		}
		/**
		 * Quadratic Cubic curves
		 * http://en.wikipedia.org/wiki/Bezier_curve
		 * @param	p0
		 * @param	p1
		 * @param	p2
		 * @param	p3
		 * @param	step
		 * @return
		 */
		public static function getBezierCubicPoints(x0:Number,y0:Number,x1:Number,y1:Number,x2:Number,y2:Number,x3:Number,y3:Number,step:Number=1/100):Vector.<Point>
		{
				var vec:Vector.<Point> = new Vector.<Point>();
				var _x:Number;
				var _y:Number;
				for (var t:Number = 0; t <= 1; t+=step) 
				{
					_x = Math.pow(1 - t, 3) * x0 + 3 * Math.pow(1 - t, 2) * t *  x1 + 3 * (1-t)*t*t*x2 + t*t*t * x3;
					_y = Math.pow(1 - t, 3) * y0 + 3 * Math.pow(1 - t, 2) * t *  y1 + 3 * (1-t)*t*t*y2 + t*t*t * y3;
					vec.push(new Point(_x,_y));
				}
				return vec;
		}
		
		public static function createUID(target:Object):String
		{
			
			var uid:String = target is String ? target.toString() : getClassName(target);
			uid = uid.concat("_",_idCounter);
			++_idCounter;
			return uid;
		}
		
		public static function getClassName(target:Object):String
		{
			if (target == null) return "null";
			var sname:String = getQualifiedClassName(target);
			return sname.substr(sname.indexOf("::") + 2);
		}
		
		/**
		 *
		 * @param	target
		 * @return
		 */
		public static function snapshoot(target:DisplayObject):BitmapData
		{
			if (target == null)
				return null;
			var r:Rectangle = target.getBounds(target);
			var bd:BitmapData = new BitmapData(r.width, r.height, true, 0x000000);
			bd.draw(target, new Matrix(1, 0, 0, 1, -r.left, -r.top));
			return bd;
		}
		/**
		 * Clone methods
		 * @param	sourceObj
		 * @return
		 */
		public static function newSibling(sourceObj:Object):*
		{
			if (sourceObj)
			{
				
				var objSibling:*;
				try
				{
					var classOfSourceObj:Class = getDefinitionByName(getQualifiedClassName(sourceObj)) as Class;
					objSibling = new classOfSourceObj();
				}
				
				catch (e:Object)
				{
				}
				
				return objSibling;
			}
			return null;
		}
		/**
		 * clone methods
		 * @param	source
		 * @return
		 */
		public static function clone(source:Object):Object
		{
			
			var clone:Object;
			if (source)
			{
				clone = newSibling(source);
				
				if (clone)
				{
					copyData(source, clone);
				}
			}
			
			return clone;
		}
		/**
		 * 
		 * @param	source
		 * @param	destination
		 */
		public static function copyData(source:Object, destination:Object):void
		{
			
			//copies data from commonly named properties and getter/setter pairs
			if ((source) && (destination))
			{
				
				try
				{
					var sourceInfo:XML = describeType(source);
					var prop:XML;
					
					for each (prop in sourceInfo.variable)
					{
						
						if (destination.hasOwnProperty(prop.@name))
						{
							destination[prop.@name] = source[prop.@name];
						}
						
					}
					
					for each (prop in sourceInfo.accessor)
					{
						if (prop.@access == "readwrite")
						{
							if (destination.hasOwnProperty(prop.@name))
							{
								destination[prop.@name] = source[prop.@name];
							}
							
						}
					}
				}
				catch (err:Object)
				{
					;
				}
			}
		}
		
		/**
		 * old clone method
		 * @param	source
		 * @return
		 */
		public static function clone2(source:Object):*
		{
			var myBA:ByteArray = new ByteArray();
			myBA.writeObject(source);
			myBA.position = 0;
			return (myBA.readObject());
		}
	
	}

}